package rs.etf.kdp.client;

import java.awt.EventQueue;

import javax.swing.DefaultListModel;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import java.awt.event.KeyEvent;
import java.awt.event.InputEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.UIManager;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Properties;

import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.Font;
import java.awt.Color;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JList;
import javax.swing.ListSelectionModel;
import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class ClientGUI {

	JFrame frmClient;
	private JDialog aboutme;
	private JDialog changeClientName;
	private JDialog changeServer;
	private JDialog reqJobsFile;
	private JTextField txtComponentsFilePath;
	private JTextField txtResultsFilePath;
	private JTextField txtConnectionsFilePath;
	private JButton btnChooseComponents;
	private JButton btnChooseConnections;
	private JButton btnChooseResult;
	private JFileChooser openConnections;
	private JFileChooser openComponents;
	private JFileChooser saveResult;
	private JLabel lblClientName;
	private JLabel lblServerAddress;
	private JLabel lblServerPort;
	private JLabel lblEmpty;
	JTextArea txtStatus;
	private JScrollPane scrollPane;
	private JTextField txtLTime;
	private JButton btnGetStatus;
	private JList lstJobs;
	static RequestedJobsCollection req;
	GeneralJobsManager jobsManager;
	DefaultListModel listModel;
	Sender sender;
	private JButton btnAbort;
	private JComboBox cmbSlmulatorType;
	static String nameOfThisClient;
	ObjectInputStream input;
	ObjectOutputStream output;
	Socket socketToServer;
	Connecter connecter;
	static Receiver rec;
	JMenuItem mntmRequestedJobs;
	JButton btnConnectToServer;
	RegistrationBuffer regBuff;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
		} catch (Throwable e) {
			e.printStackTrace();
		}
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ClientGUI window = new ClientGUI();
					window.frmClient.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public ClientGUI() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frmClient = new JFrame();
		frmClient.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent arg0) {
				req.saveToFile();
				if(connecter!=null)connecter.disconnect();
			}
		});
		frmClient.setResizable(false);
		frmClient.setTitle("Client");
		frmClient.setBounds(100, 100, 615, 461);
		frmClient.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		aboutme = new AboutMe(frmClient);
		changeClientName = new ChangeClientName(frmClient, this);
		changeServer = new ServerSetup(this);
		openComponents = new JFileChooser();
		openConnections = new JFileChooser();
		saveResult = new JFileChooser();
		req = new RequestedJobsCollection();
		reqJobsFile = new RequestedJobsFromFile(req, this);
		jobsManager = new GeneralJobsManager();
		listModel = new DefaultListModel();
		regBuff = new RegistrationBuffer();
		JMenuBar menuBar = new JMenuBar();
		frmClient.setJMenuBar(menuBar);
		
		JMenu mnFile = new JMenu("File");
		menuBar.add(mnFile);
		
		JMenuItem mntmChangeClientName = new JMenuItem("Change Client Name");
		mntmChangeClientName.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				changeClientName.setVisible(true);
			}
		});
		mntmChangeClientName.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK));
		mnFile.add(mntmChangeClientName);
		
		mntmRequestedJobs = new JMenuItem("Requested Jobs");
		mntmRequestedJobs.setEnabled(false);
		mntmRequestedJobs.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				showReq();
			}
		});
		mntmRequestedJobs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK));
		mnFile.add(mntmRequestedJobs);
		
		JMenuItem mntmExit = new JMenuItem("Exit");
		mntmExit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				frmClient.dispose();
				System.exit(0);
				
			}
		});
		mntmExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_MASK));
		mnFile.add(mntmExit);
		
		JMenu mnServer = new JMenu("Server");
		menuBar.add(mnServer);
		
		JMenuItem mntmServerSetup = new JMenuItem("Server Setup");
		mntmServerSetup.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				changeServer.setVisible(true);
			}
		});
		mntmServerSetup.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
		mnServer.add(mntmServerSetup);
		
		JMenuItem mntmSimulate = new JMenuItem("Simulate");
		mntmSimulate.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
		mnServer.add(mntmSimulate);
		
		JMenu mnHelp = new JMenu("Help");
		menuBar.add(mnHelp);
		
		JMenuItem mntmAbout = new JMenuItem("About");
		mntmAbout.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				aboutme.setVisible(true);
			}
		});
		mntmAbout.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK));
		mnHelp.add(mntmAbout);
		
		JPanel fileSelectionPanel = new JPanel();
		frmClient.getContentPane().add(fileSelectionPanel, BorderLayout.NORTH);
		fileSelectionPanel.setLayout(new GridLayout(3, 3, 10, 5));
		
		JLabel lblSelectComponentsFile = new JLabel("Components file");
		fileSelectionPanel.add(lblSelectComponentsFile);
		
		txtComponentsFilePath = new JTextField();
		fileSelectionPanel.add(txtComponentsFilePath);
		txtComponentsFilePath.setColumns(10);
		
		btnChooseComponents = new JButton("Choose Components");
		btnChooseComponents.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				getComponentsFile();
			}
		});
		fileSelectionPanel.add(btnChooseComponents);
		
		JLabel lblSelectConnectionsFile = new JLabel("Connections file");
		fileSelectionPanel.add(lblSelectConnectionsFile);
		
		txtConnectionsFilePath = new JTextField();
		fileSelectionPanel.add(txtConnectionsFilePath);
		txtConnectionsFilePath.setColumns(10);
		
		btnChooseConnections = new JButton("Choose Connections");
		btnChooseConnections.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				getConnectionsFile();
			}
		});
		fileSelectionPanel.add(btnChooseConnections);
		
		JLabel lblSelectResultsFile = new JLabel("Results file");
		fileSelectionPanel.add(lblSelectResultsFile);
		
		txtResultsFilePath = new JTextField();
		fileSelectionPanel.add(txtResultsFilePath);
		txtResultsFilePath.setColumns(10);
		
		btnChooseResult = new JButton("Choose Results");
		btnChooseResult.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				getResultsFile();
			}
		});
		fileSelectionPanel.add(btnChooseResult);
		
		JPanel statusSimulatePanel = new JPanel();
		frmClient.getContentPane().add(statusSimulatePanel, BorderLayout.WEST);
		statusSimulatePanel.setLayout(new GridLayout(4, 2, 10, 10));
		
		JLabel lblClientNameLabel = new JLabel("Client Name");
		statusSimulatePanel.add(lblClientNameLabel);
		
		lblClientName = new JLabel("NAME");
		lblClientName.setForeground(Color.BLUE);
		lblClientName.setFont(new Font("Tahoma", Font.PLAIN, 14));
		statusSimulatePanel.add(lblClientName);
		
		JLabel lblServerAddressLabel = new JLabel("Server address");
		statusSimulatePanel.add(lblServerAddressLabel);
		
		lblServerAddress = new JLabel("HOST");
		lblServerAddress.setForeground(Color.BLUE);
		lblServerAddress.setFont(new Font("Tahoma", Font.PLAIN, 14));
		statusSimulatePanel.add(lblServerAddress);
		
		JLabel lblServerPortLabel = new JLabel("Server port");
		statusSimulatePanel.add(lblServerPortLabel);
		
		lblServerPort = new JLabel("PORT");
		lblServerPort.setForeground(Color.BLUE);
		lblServerPort.setFont(new Font("Tahoma", Font.PLAIN, 14));
		statusSimulatePanel.add(lblServerPort);
		
		JLabel lblEmptyLabel = new JLabel("Empty");
		statusSimulatePanel.add(lblEmptyLabel);
		
		lblEmpty = new JLabel("EMPTY");
		lblEmpty.setForeground(Color.BLUE);
		lblEmpty.setFont(new Font("Tahoma", Font.PLAIN, 14));
		statusSimulatePanel.add(lblEmpty);
		
		JPanel simulatePanel = new JPanel();
		frmClient.getContentPane().add(simulatePanel, BorderLayout.EAST);
		simulatePanel.setLayout(new BorderLayout(0, 0));
		
		btnConnectToServer = new JButton("Send job to Server and Simulate");
		btnConnectToServer.setEnabled(false);
		btnConnectToServer.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				onSimulateClick();
			}
		});
		simulatePanel.add(btnConnectToServer, BorderLayout.SOUTH);
		
		JPanel getStatusPanel = new JPanel();
		simulatePanel.add(getStatusPanel, BorderLayout.CENTER);
		getStatusPanel.setLayout(null);
		
		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setBounds(12, 12, 189, 115);
		getStatusPanel.add(scrollPane_1);
		
		lstJobs = new JList(listModel);
		scrollPane_1.setViewportView(lstJobs);
		lstJobs.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		
		btnGetStatus = new JButton("Get Status");
		btnGetStatus.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				onGetStatusClick();
			}
		});
		btnGetStatus.setBounds(108, 143, 93, 26);
		getStatusPanel.add(btnGetStatus);
		
		btnAbort = new JButton("Abort");
		btnAbort.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				onAbortClick();
			}
		});
		btnAbort.setBounds(12, 143, 84, 26);
		getStatusPanel.add(btnAbort);
		
		fillInfo();
		
		txtStatus = new JTextArea();
		txtStatus.setRows(7);
		frmClient.getContentPane().add(txtStatus, BorderLayout.CENTER);
		
		scrollPane = new JScrollPane(txtStatus);
		frmClient.getContentPane().add(scrollPane, BorderLayout.SOUTH);
		
		JPanel timeTypePanel = new JPanel();
		frmClient.getContentPane().add(timeTypePanel, BorderLayout.CENTER);
		timeTypePanel.setLayout(null);
		
		JLabel lblLogicalTime = new JLabel("Logical Time:");
		lblLogicalTime.setBounds(12, 12, 92, 16);
		timeTypePanel.add(lblLogicalTime);
		
		txtLTime = new JTextField();
		txtLTime.setText("6");
		txtLTime.setBounds(96, 10, 100, 20);
		timeTypePanel.add(txtLTime);
		txtLTime.setColumns(10);
		
		JLabel lblType = new JLabel("Type:");
		lblType.setBounds(12, 42, 55, 16);
		timeTypePanel.add(lblType);
		
		cmbSlmulatorType = new JComboBox();
		cmbSlmulatorType.setEditable(true);
		cmbSlmulatorType.setModel(new DefaultComboBoxModel(new String[] {"Simulator Multithread", "Simulator Optimistic"}));
		cmbSlmulatorType.setSelectedIndex(0);
		cmbSlmulatorType.setBounds(57, 40, 153, 25);
		timeTypePanel.add(cmbSlmulatorType);
		
		JButton btnConnect = new JButton("Connect");
		btnConnect.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				onConnectClicked();
			}
		});
		btnConnect.setBounds(73, 181, 98, 26);
		timeTypePanel.add(btnConnect);
		
	}
	
	
	public File getComponentsFile()
	{
		int retval = openComponents.showOpenDialog(frmClient);
		if(retval == JFileChooser.APPROVE_OPTION)
		{
			File ret = openComponents.getSelectedFile();
			txtComponentsFilePath.setText(ret.getAbsolutePath());
			return ret;
		}
		return null;
	}
	
	public File getConnectionsFile()
	{
		int retval = openConnections.showOpenDialog(frmClient);
		if(retval == JFileChooser.APPROVE_OPTION)
		{
			File ret = openConnections.getSelectedFile();
			txtConnectionsFilePath.setText(ret.getAbsolutePath());
			return ret;
		}
		return null;
	}
	
	public File getResultsFile()
	{
		saveResult.setApproveButtonText("Save result in file");
		saveResult.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		int retval = saveResult.showSaveDialog(frmClient);
		if(retval == JFileChooser.APPROVE_OPTION)
		{
			File ret = saveResult.getSelectedFile();
			txtResultsFilePath.setText(ret.getAbsolutePath());
			return ret;
		}
		return null;
	}
	
	void fillInfo()
	{
		Properties pro = new Properties();
		String name = "UNKNOWN";
		String port = "UNKNOWN";
		String address = "UNKNOWN";
		
		try
		{
			pro.load(new FileInputStream("res/data.properties"));
			
			address = pro.getProperty("address");
			port = pro.getProperty("port");
			name = pro.getProperty("name");
		} catch(IOException ex)
		{
			System.out.println("ERROR LOADING PROPERTIES");
		}

		
		lblClientName.setText(name);
		ClientGUI.nameOfThisClient = name;
		lblServerAddress.setText(address);
		lblServerPort.setText(port);
	}
	
	private void showReq() {
		reqJobsFile.setVisible(true);
	}
	
	
	public void onConnectClicked()
	{
		
		if(connecter!=null)
		{
			txtStatus.append("\nYou have alredy tried to connect");
			return;
		}
		long ltime;
		try
		{
			ltime = Long.parseLong(txtLTime.getText());
		} catch(NumberFormatException ex)
		{
			txtStatus.append("\nEnter number in logic time text field. Connection is not established");
			return;
		}
		
		if(ltime<0)
		{
			txtStatus.append("\nInvalid input data. Connection is not established");
			return;
		}

		Properties data = new Properties();
		String address;
		int port;
		try {
			data.load(new FileReader("res/data.properties"));
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(null, "Unable to load property file. You will connect to \n host: 127.0.0.1\nport:888", "Error", JOptionPane.WARNING_MESSAGE);
			address = "127.0.0.1";
			port = 888;
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		String portString = data.getProperty("port");
		port = Integer.parseInt(portString);
		address = data.getProperty("address");
		
		connecter = new Connecter(address, port, this);
		connecter.setDaemon(true);
		connecter.start();
	}
	public void onSimulateClick()
	{
		//ClientContext cc = new ClientContext(null, null,null,0,null, null, null,null);
		File con = openConnections.getSelectedFile();
		File com = openComponents.getSelectedFile();
		File res = saveResult.getSelectedFile();
		long ltime;
		try
		{
			ltime = Long.parseLong(txtLTime.getText());
		} catch(NumberFormatException ex)
		{
			txtStatus.append("\nEnter number in logic time text field. Connection is not established");
			return;
		}

		if(ltime<0 || con == null || com == null ||  res==null) 
		{
			txtStatus.append("\nInvalid input data. Connection is not established");
			return;
		}
		Object type = cmbSlmulatorType.getSelectedItem();
		
		ClientContext currentContext = new ClientContext(com, con, res, ltime, type, txtStatus, listModel, jobsManager);
		
		Job thisJob = currentContext.generateJobFromCurrentContext();
		
		Sender.FileLocation newFile = new Sender.FileLocation(0, res);
		Sender.fileLocations.add(newFile);
		
		
		Sender thisSender = new Sender(currentContext, thisJob, jobsManager, res, req, connecter.getOutput(), connecter.getInput(), regBuff);
		this.sender = thisSender;
		thisSender.start();
			
	}
	
	public void onAbortClick()
	{
		long id = (Long)lstJobs.getSelectedValue();
		
		sender.sendAbortMessage(id);
		
	}
	
	public void onGetStatusClick()
	{
		long id = (Long)lstJobs.getSelectedValue();
		
		jobsManager.getCurrentStatusOnServer(id);
	}
	
	
}
